/********************************************************************************
 * Copyright (c)2016-2020, ShenZhen WanJiaAn Technology CO.,LTD.
 * All Rights Reserved.
 *
 * File Name     : audio.c
 * Author        : joki.zhu
 * Date          : 2019-10-11
 *
 * Record        :
 * 1.Date        : 2019-10-11
 *   Author      : joki.zhu
 *   Modification: Created file
 ********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "Audio.h"
#include "app_common.h"
#include "protocol.h"

/****************************************************************************************/
#define TAG	    "AUDIO"

#define MAX_AUDIO_BUFFER        (2)
#define MAX_AUDIO_BUFFER_SIZE   (1024)

/****************************************************************************************/
typedef struct {
    AV_U8       buffMax;
    AV_U32      buffSize;  
    MEDIA_PACK_AUDIO stPackAudio[MAX_AUDIO_BUFFER];
}AUDIO_BUFF_INFO_S;

/****************************************************************************************/
static AUDIO_REG_ATTR_S gs_stAudioRegAttr[AUDIO_AENC_CHN_MAX_NUM] = {0};
static AUDIO_BUFF_INFO_S gs_stAudioBuffInfo;


/****************************************************************************************/

static int OnAudioAencData(int chn, void *data)
{
    int ret = 0;
    static int cnt = 0;
    struct timespec ts;

    if (cnt == gs_stAudioBuffInfo.buffMax) {
        cnt=0;
    }

#ifdef __HuaweiLite__
    ts.tv_sec = 2;  
    ts.tv_nsec = 0;
#else
    if (clock_gettime(CLOCK_REALTIME, &ts) < 0) {
        LOGE("clock_gettime CLOCK_REALTIME failed:%s\n", strerror(errno));
        return 0;
    }
    ts.tv_sec += 2;
#endif /* __HuaweiLite__ */  
    ret = sem_timedwait(&gs_stAudioBuffInfo.stPackAudio[cnt].writeSem, &ts);
    if (ret < 0) {
        if (errno == ETIMEDOUT) {
            LOGE("sem_timedwait() timed out\n");
            return -1;
        } else {
            LOGD("sem_timedwait() error\n");
            return -1;
        }
    }
    
    ret = AudioGetOpsHandle()->audio_copy_data(chn, data, &gs_stAudioBuffInfo.stPackAudio[cnt], gs_stAudioBuffInfo.buffSize);
    if (ret < 0) {
        LOGE("copy to buf failed\n");
        return -1;
    }

    sem_post(&gs_stAudioBuffInfo.stPackAudio[cnt].readSem);
    cnt++;

    return 0;
}

void *tskAudioStreamSend(void *args)
{
    int chn = 0;

    int index = 0;
    int bufCnt = 0;
    
    SET_THREAD_NAME("tskAudioStreamSend");
    pthread_detach(pthread_self());
    
    while (1)
    {
        if (bufCnt == (gs_stAudioBuffInfo.buffMax)) {
            bufCnt = 0;
        }
        
        sem_wait(&gs_stAudioBuffInfo.stPackAudio[bufCnt].readSem);
        
        if(gs_stAudioBuffInfo.stPackAudio[bufCnt].frameSize == 0) {
            sem_post(&gs_stAudioBuffInfo.stPackAudio[bufCnt].writeSem);
            continue;
        }
        
        index = 0;	
		AV_MUTEX_LOCK(gs_stAudioRegAttr[chn].mutexLock);
        for (index = 0; index < AUDIO_REG_MAX_NUM; index++)
        {
            if (NULL != gs_stAudioRegAttr[chn].pfnRegAudioCbArry[index])
            {
                gs_stAudioRegAttr[chn].pfnRegAudioCbArry[index](chn, gs_stAudioBuffInfo.stPackAudio[bufCnt]);
            }
        }
        AV_MUTEX_UNLOCK(gs_stAudioRegAttr[chn].mutexLock);

        sem_post(&gs_stAudioBuffInfo.stPackAudio[bufCnt].writeSem);
        bufCnt++;
    }

    pthread_exit(0);
}

static int audio_register_attr_init(void)
{
    static char bInit = 0;

    if (!bInit) 
    {
        int i = 0;
        for (i = 0; i < AUDIO_AENC_CHN_MAX_NUM; i++) {
            AV_MUTEX_INIT_LOCK(gs_stAudioRegAttr[i].mutexLock);
            memset(gs_stAudioRegAttr[i].pfnRegAudioCbArry, NULL, sizeof(gs_stAudioRegAttr[i].pfnRegAudioCbArry));
        }
        bInit = 1;
	}

    return 0;
}

int AudioTalkPlaySet(int encType ,int sampRate,  int bitWidth, uint volume)
{
    return AudioGetOpsHandle()->talkback_start();
}

int AudioTalkPlayFinish(void)
{
    return AudioGetOpsHandle()->talkback_stop();
}

int AudioTalkPlayFrame(unsigned char* pDataAdd,int length)
{
    return AudioGetOpsHandle()->talkback_data(pDataAdd, length, AUDIO_TYPE_PCM);
}

int AudioRegisterRecvData(int chn, AudioRecvCallback pfnRcvAudioCb)
{
    int index = 0;

    audio_register_attr_init();

    if (chn < 0 || chn >= AUDIO_AENC_CHN_MAX_NUM) {
        printf("chn (%d) is invalid!\n");
        return -1;
    }

    for (index = 0; index < AUDIO_REG_MAX_NUM; index++)
    {
        if (NULL == gs_stAudioRegAttr[chn].pfnRegAudioCbArry[index]) {
            break;
        } else if (pfnRcvAudioCb == gs_stAudioRegAttr[chn].pfnRegAudioCbArry[index]) {
            printf("This function has been registered !\n");
            return -1;
        }
    }

    AV_MUTEX_LOCK(gs_stAudioRegAttr[chn].mutexLock);

    if (index >= 0 && index < AUDIO_REG_MAX_NUM)
    {
        gs_stAudioRegAttr[chn].pfnRegAudioCbArry[index] = pfnRcvAudioCb;
    }

    AV_MUTEX_UNLOCK(gs_stAudioRegAttr[chn].mutexLock);

    return 0;
}

int AudioUnRegisterRecvData(int chn, AudioRecvCallback pfnRcvAudioCb)
{
    int index = 0;

    AV_MUTEX_LOCK(gs_stAudioRegAttr[chn].mutexLock);

    for (index = 0; index < AUDIO_REG_MAX_NUM; index++)
    {
        if (pfnRcvAudioCb == gs_stAudioRegAttr[chn].pfnRegAudioCbArry[index])
        {
            gs_stAudioRegAttr[chn].pfnRegAudioCbArry[index] = NULL;
            break;
        }	
    }
    AV_MUTEX_UNLOCK(gs_stAudioRegAttr[chn].mutexLock);

    return 0;
}

extern AUDIO_OPS_FUNC_S *hisi_liteos_audio_ops_handle(void);

AUDIO_OPS_FUNC_S *AudioGetOpsHandle(void)
{
    return hisi_liteos_audio_ops_handle();
}

int AudioInit(void)
{
#ifdef MINI_APP
    return 0;
#endif /* MINI_APP */

    int i = 0;
    int ret = 0;
    void * audioBuf = NULL;
    int abuffSize  = 0;
    int offset = 0;
    int retErrNum = 0;    
    pthread_t audioStreamPid;
    
    audio_register_attr_init();

    /* audio buffer malloc */
    memset(&gs_stAudioBuffInfo, 0, sizeof(gs_stAudioBuffInfo));
    gs_stAudioBuffInfo.buffMax = MAX_AUDIO_BUFFER;
    gs_stAudioBuffInfo.buffSize = MAX_AUDIO_BUFFER_SIZE;  
    abuffSize = gs_stAudioBuffInfo.buffMax * gs_stAudioBuffInfo.buffSize;
    audioBuf = malloc(abuffSize);
    if(audioBuf == NULL) {
        LOGE("malloc audio info error\n");
        goto err_out0;
    }
    memset(audioBuf, 0, abuffSize);
	for (i = 0; i < gs_stAudioBuffInfo.buffMax; i++) {
        gs_stAudioBuffInfo.stPackAudio[i].pFrameData = ((AV_VOID *)audioBuf + offset);
        offset += gs_stAudioBuffInfo.buffSize;
        retErrNum = sem_init(&gs_stAudioBuffInfo.stPackAudio[i].writeSem, 0, 1);
        retErrNum = sem_init(&gs_stAudioBuffInfo.stPackAudio[i].readSem, 0, 0);
        if (retErrNum < 0) {
            LOGE("sem_init readSem failed:%s\n", strerror(errno));
            goto err_out1;
        }
    } 

    /* audio init */
    ret = AudioGetOpsHandle()->init();
    if (ret < 0) {
        LOGE("audio init failed\n");
        goto err_out1;
    }

    /* audio stream task run */
    ret = pthread_create(&audioStreamPid, NULL, &tskAudioStreamSend, NULL);
    if (ret != 0) {
        LOGE("pthread_create tskAudioStreamSend failed!\n");
        goto err_out1;
    }

    /* audio aenc start */
    ret = AudioGetOpsHandle()->audio_aenc_start(0, OnAudioAencData);
    if (ret < 0) {
        LOGE("audio_aenc_start failed\n");
        goto err_out1;
    }
    
    return 0;
    
err_out1:
    free(audioBuf);
err_out0:
    return -1;

}

int AudioDeinit(void)
{
#ifdef MINI_APP
    return 0;
#endif /* MINI_APP */

    int i = 0;
    int ret = 0;

    /* audio aenc stop */
    ret = AudioGetOpsHandle()->audio_aenc_stop(0, OnAudioAencData);
    AV_RETURN_WITH_INFO_IF_EXPR_FALSE(ret == 0, -1, "audio_aenc_stop");

    /* audio deinit */
    ret = AudioGetOpsHandle()->deinit();
    AV_RETURN_WITH_INFO_IF_EXPR_FALSE(ret == 0, -1, "audio deinit");

    /* audio buffer free */
    for (i = 0; i < gs_stAudioBuffInfo.buffMax; i++)
    {
        if (NULL != gs_stAudioBuffInfo.stPackAudio[i].pFrameData) {
            free(gs_stAudioBuffInfo.stPackAudio[i].pFrameData);
            gs_stAudioBuffInfo.stPackAudio[i].pFrameData = NULL;
        }
    }

    return 0;
}

